技巧98 删除在构建中加入的密码

在企业环境中构建镜像的时候,经常需要通过密钥和凭证来获取数据。如果使用Dockerfile来构建应用,那么这些密码大体上会出现在历史中,即使事后进行了删除。

这可能是个安全问题:如果有人获取了镜像,他们可能获取之前层中的密码。

问题

想要从镜像历史中移除文件。

解决方案

使用 docker-squash 从镜像中移除层。

对于历史中的工作,有些简单的办法来处理。比如,你可以像代码清单14-8所示的这样在使用密码的时候就把它给删除了。

代码清单14-8  简单粗暴地直接不把密码留在层里

FROM ubuntu
RUN echo mysecret > secretfile && command_using_secret && rm secretfile

这种方法有几个缺点。它需要把密码写在Dockerfile里,所以在你的源码控制系统里可能是明文。

为了避免此问题,需要把该文件加入源码控制的.gitignore(或者类似)文件中,然后在构建镜像的时候加入镜像。这就把文件加入了单独的层,不是轻易可以从构建出来的镜像中简单移除的。

最终,如果使用环境变量来存储密码,这也可能带来安全风险。这些变量可能简单地设置在一些不安全的持久存储层(比如Jenkins任务)里。在任何情况下,都可能有人给你一个镜像然后让你从上面拉取密码。首先我们通过一个简单的例子来展示该问题,然后我们展示一种从基础层移除的方法。

1.带密码的镜像

代码清单14-9所示的Dockerfile会使用一个叫secret_file的文件作为你放入其中的密码数据的占位符来创建镜像。

代码清单14-9 带有密码的Dockerfile

FROM ubuntu
CMD ls /  ⇽--- 为了节省一点时间,我们使用了文件列表命令重载了默认的命令,这样会演示文件是否在历史中
ADD /secret_file secret_file  ⇽--- 密码文件加入镜像构建(必须和Dockerfile共同存在于当前目录中)
RUN cat /secret_file  ⇽--- 把密码文件当成构建的一部分。在本例中,我们简单地用了cat命令来输出文件,但是这个命令也可能是git clone或者其他更有用的命令
RUN rm /secret_file  ⇽--- 移除密码文件

现在可以开始构建了,构建结果可称之为密码构建,如代码清单14-10所示。

代码清单14-10 使用密码构建简单的Docker镜像

$ echo mysecret > secret_file
$ docker build -t secret_build .
Sending build context to Docker daemon 5.12 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu
 ---> 08881219da4a
Step 1 : CMD ls /
 ---> Running in 7864e2311699
 ---> 5b39a3cba0b0
Removing intermediate container 7864e2311699
Step 2 : ADD /secret_file secret_file
 ---> a00886ff1240
Removing intermediate container 4f279a2af398
Step 3 : RUN cat /secret_file
 ---> Running in 601fdf2659dd
My secret
 ---> 2a4238c53408
Removing intermediate container 601fdf2659dd
Step 4 : RUN rm /secret_file
 ---> Running in 240a4e57153b
 ---> b8a62a826ddf
Removing intermediate container 240a4e57153b
Successfully built b8a62a826ddf

该镜像一旦构建完毕,可使用技巧27来展示密码文件,如代码清单14-11所示。

代码清单14-11 给每一步打标签,并且展示带有密码的那一层

$ x=0; for id in $(docker history -q secret_build:latest);do ((x++)); docker tag $id secret_build:step_$x; done  ⇽--- 演示密码文件在镜像的标签之中
$ docker run secret_build:step_3 cat /secret_file'  ⇽--- 把每一步按照数字顺序打上标签
 mysecret
2.使用docker-squash来移除密码

现在已经看到即使在最终产品中没有密码,密码也可能存在于镜像的历史中。这就是 docker-squash 有用武之地的地方了——它在历史中移除中间的层但是保持了Dockerfile命令(如 CMDPORTENV 以及其他)和原始的基础层。

代码清单14-12下载、安装并且使用了 docke-squash 来比较了处理前和处理后的镜像。

代码清单14-12 用docker-squash来减少镜像的层

$ wget -qO- https://github.com/jwilder/docker-squash/releases/download/v0.2.0/docker-squash-linux-amd64-v0.2.0.tar.gz | \
  tar -zxvf -  && mv docker-squash /usr/local/bin  ⇽--- 安装docker-squash
 $ docker save secret_build:latest | \  ⇽--- 
   docker-squash -t secret_build_squashed | \
   docker load  ⇽--- 把镜像保存在docker-squash要处理的TAR文件里,之后载入结果镜像,打标签为“secret_build_squashed”
 $ docker history secret_build_squashed  ⇽--- 处理后的镜像的历史里没有secret_file的记录
 IMAGE         CREATED        CREATED BY                          SIZE
ee41518cca25  2 seconds ago  /bin/sh -c #(nop) CMD ["/bin/sh" "  0 B
b1c283b3b20a  2 seconds ago  /bin/sh -c #(nop) CMD ["/bin/bash   0 B
f443d173e026  2 seconds ago  /bin/sh -c #(squash) from 93c22f56  2.647 kB
93c22f563196  2 weeks ago    /bin/sh -c #(nop) ADD file:7529d28  128.9 MB
$ docker history secret_build  ⇽--- 原始镜像仍然有secret_file
 IMAGE         CREATED        CREATED BY                          SIZE
b8a62a826ddf  3 seconds ago  /bin/sh -c rm /secret_file          0 B
2a4238c53408  3 seconds ago  /bin/sh -c cat /secret_file         0 B
a00886ff1240  9 seconds ago  /bin/sh -c #(nop) ADD file:69e77f6  10 B
5b39a3cba0b0  9 seconds ago  /bin/sh -c #(nop) CMD ["/bin/sh" "  0 B
08881219da4a  2 weeks ago    /bin/sh -c #(nop)  CMD ["/bin/bash  0 B
6a4ec4bddc58  2 weeks ago    /bin/sh -c mkdir -p /run/systemd &  7 B
98697477f76a  2 weeks ago    /bin/sh -c sed -i 's/^#\s*\(deb.*u  1.895 kB
495ec797e6ba  2 weeks ago    /bin/sh -c rm -rf /var/lib/apt/lis  0 B
e3aa81f716f6  2 weeks ago    /bin/sh -c set -xe && echo '#!/bin  745 B
93c22f563196  2 weeks ago    /bin/sh -c #(nop) ADD file:7529d28  128.9 MB
$ docker run secret_build_squashed ls /secret_file  ⇽--- 展示secret_file不在处理后的镜像里
 ls: cannot access '/secret_file': No such file or directory
$ docker run f443d173e026 ls /secret_file  ⇽--- 展示secret_file不在处理后的“处理”层里
 ls: cannot access '/secret_file': No such file or directory
3.对于“消失的“镜像层的标注

Docker在1.10版本改变了层的属性。从那时候起,下载的镜像在历史中展示为“”。这是预料中的,Docker以此改变来增强镜像历史的安全性。

仍然可以通过把下载的镜像层进行 docker save 然后解压TAR文件的方式来获取其内容。代码清单14-13给出了一个对已下载Ubuntu镜像执行此操作的例子。

代码清单14-13 下载镜像中“消失的”层

$ docker history ubuntu  ⇽--- 使用docker history命令来展示Ubuntu镜像的层历史
 IMAGE         CREATED       CREATED BY                               SIZE
104bec311bcd  2 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]     0 B
<missing>     2 weeks ago   /bin/sh -c mkdir -p /run/systemd && ech  7 B
<missing>     2 weeks ago   /bin/sh -c sed -i 's/^#\s*\(deb.*univer  1.9 kB
<missing>     2 weeks ago   /bin/sh -c rm -rf /var/lib/apt/lists/*   0 B
<missing>     2 weeks ago   /bin/sh -c set -xe   && echo '#!/bin/sh  745 B
<missing>     2 weeks ago   /bin/sh -c #(nop) ADD file:7529d28035b4  129 MB
$ docker save ubuntu | tar -xf -  ⇽--- 使用docker save命令来输出TAR文件到镜像层,直接通过管道来进行tar和解压
 $ find . | grep tar$  ⇽--- 展示TAR文件中只包含在此层的修改
 ./042e55060780206b2ceabe277a8beb9b10f48262a876fd21b495af318f2f2352/layer.tar
./1037e0a8442d212d5cc63d1bc706e0e82da0eaafd62a2033959cfc629f874b28/layer.tar
./25f649b30070b739bc2aa3dd877986bee4de30e43d6260b8872836cdf549fcfc/layer.tar
./3094e87864d918dfdb2502e3f5dc61ae40974cd957d5759b80f6df37e0e467e4/layer.tar
./41b8111724ab7cb6246c929857b0983a016f11346dcb25a551a778ef0cd8af20/layer.tar
./4c3b7294fe004590676fa2c27a9a952def0b71553cab4305aeed4d06c3b308ea/layer.tar
./5d1be8e6ec27a897e8b732c40911dcc799b6c043a8437149ab021ff713e1044f/layer.tar
./a594214bea5ead6d6774f7a09dbd7410d652f39cc4eba5c8571d5de3bcbe0057/layer.tar
./b18fcc335f7aeefd87c9d43db2888bf6ea0ac12645b7d2c33300744c770bcec7/layer.tar
./d899797a09bfcc6cb8e8a427bb358af546e7c2b18bf8e2f7b743ec36837b42f2/layer.tar
./ubuntu.tar
$ tar -tvf
➥ ./4c3b7294fe004590676fa2c27a9a952def0b71553cab4305aeed4d06c3b308ea
➥ /layer.tar
drwxr-xr-x  0 0      0           0 15 Dec 17:45 etc/
drwxr-xr-x  0 0      0           0 15 Dec 17:45 etc/apt/
-rw-r--r--  0 0      0        1895 15 Dec 17:45 etc/apt/sources.list

讨论

尽管在某种程度上和技巧52相似,在最终结果上来说使用专用工具有所不同。在之前的解决方案中,可以看到元数据层(比如CMD)得以保留,但是之前的技巧会完全废弃它们。所以需要手动通过另外的Dockerfile来重建元数据层。

这种行为意味着docker-squash工具可以用来在镜像发布到仓库之前自动进行清理,如果你倾向于不相信用户会正确使用镜像构建中的密码数据——这些镜像应该都可以正常工作。

如上所述,你应该怀疑用户会把密码放在元数据层——环境变量尤其是个威胁,在最终的镜像中可能会得到很好的保留。

results matching ""

    No results matching ""